/**
 * echarts图表基类
 *
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *
 */


// 图形依赖
var ImageShape = require('zrender/shape/Image');
var IconShape = require('../util/shape/Icon');
var MarkLineShape = require('../util/shape/MarkLine');
var SymbolShape = require('../util/shape/Symbol');
var PolylineShape = require('zrender/shape/Polyline');
var ShapeBundle = require('zrender/shape/ShapeBundle');
var ecConfig = require('../config');
var ecData = require('../util/ecData');
var ecAnimation = require('../util/ecAnimation');
var ecEffect = require('../util/ecEffect');
var accMath = require('../util/accMath');
var ComponentBase = require('../component/base');
var EdgeBundling = require('../layout/EdgeBundling');
var zrUtil = require('zrender/tool/util');
var zrArea = require('zrender/tool/area');
// Some utility functions
function isCoordAvailable(coord) {
    return coord.x != null && coord.y != null;
}
function Base(ecTheme, messageCenter, zr, option, myChart) {
    ComponentBase.call(this, ecTheme, messageCenter, zr, option, myChart);
    var self = this;
    this.selectedMap = {};
    this.lastShapeList = [];
    this.shapeHandler = {
        onclick: function () {
            self.isClick = true;
        },
        ondragover: function (param) {
            // 返回触发可计算特性的图形提示
            var calculableShape = param.target;
            calculableShape.highlightStyle = calculableShape.highlightStyle || {};
            // 备份特出特性
            var highlightStyle = calculableShape.highlightStyle;
            var brushType = highlightStyle.brushTyep;
            var strokeColor = highlightStyle.strokeColor;
            var lineWidth = highlightStyle.lineWidth;
            highlightStyle.brushType = 'stroke';
            highlightStyle.strokeColor = self.ecTheme.calculableColor || ecConfig.calculableColor;
            highlightStyle.lineWidth = calculableShape.type === 'icon' ? 30 : 10;
            self.zr.addHoverShape(calculableShape);
            setTimeout(function () {
                // 复位
                if (highlightStyle) {
                    highlightStyle.brushType = brushType;
                    highlightStyle.strokeColor = strokeColor;
                    highlightStyle.lineWidth = lineWidth;
                }
            }, 20);
        },
        ondrop: function (param) {
            // 排除一些非数据的拖拽进入
            if (ecData.get(param.dragged, 'data') != null) {
                self.isDrop = true;
            }
        },
        ondragend: function () {
            self.isDragend = true;
        }
    };
}
/**
     * 基类方法
     */
Base.prototype = {
    /**
         * 图形拖拽特性 
         */
    setCalculable: function (shape) {
        shape.dragEnableTime = this.ecTheme.DRAG_ENABLE_TIME || ecConfig.DRAG_ENABLE_TIME;
        shape.ondragover = this.shapeHandler.ondragover;
        shape.ondragend = this.shapeHandler.ondragend;
        shape.ondrop = this.shapeHandler.ondrop;
        return shape;
    },
    /**
         * 数据项被拖拽进来
         */
    ondrop: function (param, status) {
        if (!this.isDrop || !param.target || status.dragIn) {
            // 没有在当前实例上发生拖拽行为或者已经被认领了则直接返回
            return;
        }
        var target = param.target;
        // 拖拽安放目标
        var dragged = param.dragged;
        // 当前被拖拽的图形对象
        var seriesIndex = ecData.get(target, 'seriesIndex');
        var dataIndex = ecData.get(target, 'dataIndex');
        var series = this.series;
        var data;
        var legend = this.component.legend;
        if (dataIndex === -1) {
            // 落到calculableCase上，数据被拖拽进某个饼图|雷达|漏斗，增加数据
            if (ecData.get(dragged, 'seriesIndex') == seriesIndex) {
                // 自己拖拽到自己
                status.dragOut = status.dragIn = status.needRefresh = true;
                this.isDrop = false;
                return;
            }
            data = {
                value: ecData.get(dragged, 'value'),
                name: ecData.get(dragged, 'name')
            };
            // 修饼图数值不为负值
            if (this.type === ecConfig.CHART_TYPE_PIE && data.value < 0) {
                data.value = 0;
            }
            var hasFind = false;
            var sData = series[seriesIndex].data;
            for (var i = 0, l = sData.length; i < l; i++) {
                if (sData[i].name === data.name && sData[i].value === '-') {
                    series[seriesIndex].data[i].value = data.value;
                    hasFind = true;
                }
            }
            !hasFind && series[seriesIndex].data.push(data);
            legend && legend.add(data.name, dragged.style.color || dragged.style.strokeColor);
        } else {
            // 落到数据item上，数据被拖拽到某个数据项上，数据修改
            data = series[seriesIndex].data[dataIndex] || '-';
            if (data.value != null) {
                if (data.value != '-') {
                    series[seriesIndex].data[dataIndex].value = accMath.accAdd(series[seriesIndex].data[dataIndex].value, ecData.get(dragged, 'value'));
                } else {
                    series[seriesIndex].data[dataIndex].value = ecData.get(dragged, 'value');
                }
                if (this.type === ecConfig.CHART_TYPE_FUNNEL || this.type === ecConfig.CHART_TYPE_PIE) {
                    legend && legend.getRelatedAmount(data.name) === 1 && this.component.legend.del(data.name);
                    data.name += this.option.nameConnector + ecData.get(dragged, 'name');
                    legend && legend.add(data.name, dragged.style.color || dragged.style.strokeColor);
                }
            } else {
                if (data != '-') {
                    series[seriesIndex].data[dataIndex] = accMath.accAdd(series[seriesIndex].data[dataIndex], ecData.get(dragged, 'value'));
                } else {
                    series[seriesIndex].data[dataIndex] = ecData.get(dragged, 'value');
                }
            }
        }
        // 别status = {}赋值啊！！
        status.dragIn = status.dragIn || true;
        // 处理完拖拽事件后复位
        this.isDrop = false;
        var self = this;
        setTimeout(function () {
            self.zr.trigger('mousemove', param.event);
        }, 300);
        return;
    },
    /**
         * 数据项被拖拽出去
         */
    ondragend: function (param, status) {
        if (!this.isDragend || !param.target || status.dragOut) {
            // 没有在当前实例上发生拖拽行为或者已经被认领了则直接返回
            return;
        }
        var target = param.target;
        // 被拖拽图形元素
        var seriesIndex = ecData.get(target, 'seriesIndex');
        var dataIndex = ecData.get(target, 'dataIndex');
        var series = this.series;
        // 删除被拖拽走的数据
        if (series[seriesIndex].data[dataIndex].value != null) {
            series[seriesIndex].data[dataIndex].value = '-';
            // 清理可能有且唯一的legend data
            var name = series[seriesIndex].data[dataIndex].name;
            var legend = this.component.legend;
            if (legend && legend.getRelatedAmount(name) === 0) {
                legend.del(name);
            }
        } else {
            series[seriesIndex].data[dataIndex] = '-';
        }
        // 别status = {}赋值啊！！
        status.dragOut = true;
        status.needRefresh = true;
        // 处理完拖拽事件后复位
        this.isDragend = false;
        return;
    },
    /**
         * 图例选择
         */
    onlegendSelected: function (param, status) {
        var legendSelected = param.selected;
        for (var itemName in this.selectedMap) {
            if (this.selectedMap[itemName] != legendSelected[itemName]) {
                // 有一项不一致都需要重绘
                status.needRefresh = true;
            }
            this.selectedMap[itemName] = legendSelected[itemName];
        }
        return;
    },
    /**
         * 折线图、柱形图公用方法
         */
    _buildPosition: function () {
        this._symbol = this.option.symbolList;
        this._sIndex2ShapeMap = {};
        // series拐点图形类型，seriesIndex索引到shape type
        this._sIndex2ColorMap = {};
        // series默认颜色索引，seriesIndex索引到color
        this.selectedMap = {};
        this.xMarkMap = {};
        var series = this.series;
        // 水平垂直双向series索引 ，position索引到seriesIndex
        var _position2sIndexMap = {
            top: [],
            bottom: [],
            left: [],
            right: [],
            other: []
        };
        var xAxisIndex;
        var yAxisIndex;
        var xAxis;
        var yAxis;
        for (var i = 0, l = series.length; i < l; i++) {
            if (series[i].type === this.type) {
                series[i] = this.reformOption(series[i]);
                this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
                xAxisIndex = series[i].xAxisIndex;
                yAxisIndex = series[i].yAxisIndex;
                xAxis = this.component.xAxis.getAxis(xAxisIndex);
                yAxis = this.component.yAxis.getAxis(yAxisIndex);
                if (xAxis.type === ecConfig.COMPONENT_TYPE_AXIS_CATEGORY) {
                    _position2sIndexMap[xAxis.getPosition()].push(i);
                } else if (yAxis.type === ecConfig.COMPONENT_TYPE_AXIS_CATEGORY) {
                    _position2sIndexMap[yAxis.getPosition()].push(i);
                } else {
                    _position2sIndexMap.other.push(i);
                }
            }
        }
        // console.log(_position2sIndexMap);
        for (var position in _position2sIndexMap) {
            if (_position2sIndexMap[position].length > 0) {
                this._buildSinglePosition(position, _position2sIndexMap[position]);
            }
        }
        this.addShapeList();
    },
    /**
         * 构建单个方向上的折线图、柱形图公用方法
         *
         * @param {number} seriesIndex 系列索引
         */
    _buildSinglePosition: function (position, seriesArray) {
        var mapData = this._mapData(seriesArray);
        var locationMap = mapData.locationMap;
        var maxDataLength = mapData.maxDataLength;
        if (maxDataLength === 0 || locationMap.length === 0) {
            return;
        }
        switch (position) {
        case 'bottom':
        case 'top':
            this._buildHorizontal(seriesArray, maxDataLength, locationMap, this.xMarkMap);
            break;
        case 'left':
        case 'right':
            this._buildVertical(seriesArray, maxDataLength, locationMap, this.xMarkMap);
            break;
        case 'other':
            this._buildOther(seriesArray, maxDataLength, locationMap, this.xMarkMap);
            break;
        }
        for (var i = 0, l = seriesArray.length; i < l; i++) {
            this.buildMark(seriesArray[i]);
        }
    },
    /**
         * 数据整形，折线图、柱形图公用方法
         * 数组位置映射到系列索引
         */
    _mapData: function (seriesArray) {
        var series = this.series;
        var serie;
        // 临时映射变量
        var dataIndex = 0;
        // 堆积数据所在位置映射
        var stackMap = {};
        // 堆积数据位置映射，堆积组在二维中的第几项
        var magicStackKey = '__kener__stack__';
        // 堆积命名，非堆积数据安单一堆积处理
        var stackKey;
        // 临时映射变量
        var serieName;
        // 临时映射变量
        var legend = this.component.legend;
        var locationMap = [];
        // 需要返回的东西：数组位置映射到系列索引
        var maxDataLength = 0;
        // 需要返回的东西：最大数据长度
        var iconShape;
        // 计算需要显示的个数和分配位置并记在下面这个结构里
        for (var i = 0, l = seriesArray.length; i < l; i++) {
            serie = series[seriesArray[i]];
            serieName = serie.name;
            this._sIndex2ShapeMap[seriesArray[i]] = this._sIndex2ShapeMap[seriesArray[i]] || this.query(serie, 'symbol') || this._symbol[i % this._symbol.length];
            if (legend) {
                this.selectedMap[serieName] = legend.isSelected(serieName);
                this._sIndex2ColorMap[seriesArray[i]] = legend.getColor(serieName);
                iconShape = legend.getItemShape(serieName);
                if (iconShape) {
                    // 回调legend，换一个更形象的icon
                    var style = iconShape.style;
                    if (this.type == ecConfig.CHART_TYPE_LINE) {
                        style.iconType = 'legendLineIcon';
                        style.symbol = this._sIndex2ShapeMap[seriesArray[i]];
                    } else if (serie.itemStyle.normal.barBorderWidth > 0) {
                        var highlightStyle = iconShape.highlightStyle;
                        style.brushType = 'both';
                        style.x += 1;
                        style.y += 1;
                        style.width -= 2;
                        style.height -= 2;
                        style.strokeColor = highlightStyle.strokeColor = serie.itemStyle.normal.barBorderColor;
                        highlightStyle.lineWidth = 3;
                    }
                    legend.setItemShape(serieName, iconShape);
                }
            } else {
                this.selectedMap[serieName] = true;
                this._sIndex2ColorMap[seriesArray[i]] = this.zr.getColor(seriesArray[i]);
            }
            if (this.selectedMap[serieName]) {
                stackKey = serie.stack || magicStackKey + seriesArray[i];
                if (stackMap[stackKey] == null) {
                    stackMap[stackKey] = dataIndex;
                    locationMap[dataIndex] = [seriesArray[i]];
                    dataIndex++;
                } else {
                    // 已经分配了位置就推进去就行
                    locationMap[stackMap[stackKey]].push(seriesArray[i]);
                }
            }
            // 兼职帮算一下最大长度
            maxDataLength = Math.max(maxDataLength, serie.data.length);
        }
        /* 调试输出
            var s = '';
            for (var i = 0, l = maxDataLength; i < l; i++) {
                s = '[';
                for (var j = 0, k = locationMap.length; j < k; j++) {
                    s +='['
                    for (var m = 0, n = locationMap[j].length - 1; m < n; m++) {
                        s += series[locationMap[j][m]].data[i] + ','
                    }
                    s += series[locationMap[j][locationMap[j].length - 1]]
                         .data[i];
                    s += ']'
                }
                s += ']';
                console.log(s);
            }
            console.log(locationMap)
            */
        return {
            locationMap: locationMap,
            maxDataLength: maxDataLength
        };
    },
    _calculMarkMapXY: function (xMarkMap, locationMap, xy) {
        var series = this.series;
        for (var j = 0, k = locationMap.length; j < k; j++) {
            for (var m = 0, n = locationMap[j].length; m < n; m++) {
                var seriesIndex = locationMap[j][m];
                var valueIndex = xy == 'xy' ? 0 : '';
                var grid = this.component.grid;
                var tarMark = xMarkMap[seriesIndex];
                if (xy.indexOf('x') != '-1') {
                    if (tarMark['counter' + valueIndex] > 0) {
                        tarMark['average' + valueIndex] = tarMark['sum' + valueIndex] / tarMark['counter' + valueIndex];
                    }
                    var x = this.component.xAxis.getAxis(series[seriesIndex].xAxisIndex || 0).getCoord(tarMark['average' + valueIndex]);
                    tarMark['averageLine' + valueIndex] = [
                        [
                            x,
                            grid.getYend()
                        ],
                        [
                            x,
                            grid.getY()
                        ]
                    ];
                    tarMark['minLine' + valueIndex] = [
                        [
                            tarMark['minX' + valueIndex],
                            grid.getYend()
                        ],
                        [
                            tarMark['minX' + valueIndex],
                            grid.getY()
                        ]
                    ];
                    tarMark['maxLine' + valueIndex] = [
                        [
                            tarMark['maxX' + valueIndex],
                            grid.getYend()
                        ],
                        [
                            tarMark['maxX' + valueIndex],
                            grid.getY()
                        ]
                    ];
                    tarMark.isHorizontal = false;
                }
                valueIndex = xy == 'xy' ? 1 : '';
                if (xy.indexOf('y') != '-1') {
                    if (tarMark['counter' + valueIndex] > 0) {
                        tarMark['average' + valueIndex] = tarMark['sum' + valueIndex] / tarMark['counter' + valueIndex];
                    }
                    var y = this.component.yAxis.getAxis(series[seriesIndex].yAxisIndex || 0).getCoord(tarMark['average' + valueIndex]);
                    tarMark['averageLine' + valueIndex] = [
                        [
                            grid.getX(),
                            y
                        ],
                        [
                            grid.getXend(),
                            y
                        ]
                    ];
                    tarMark['minLine' + valueIndex] = [
                        [
                            grid.getX(),
                            tarMark['minY' + valueIndex]
                        ],
                        [
                            grid.getXend(),
                            tarMark['minY' + valueIndex]
                        ]
                    ];
                    tarMark['maxLine' + valueIndex] = [
                        [
                            grid.getX(),
                            tarMark['maxY' + valueIndex]
                        ],
                        [
                            grid.getXend(),
                            tarMark['maxY' + valueIndex]
                        ]
                    ];
                    tarMark.isHorizontal = true;
                }
            }
        }
    },
    /**
         * 添加文本 
         */
    addLabel: function (tarShape, serie, data, name, orient) {
        // 多级控制
        var queryTarget = [
            data,
            serie
        ];
        var nLabel = this.deepMerge(queryTarget, 'itemStyle.normal.label');
        var eLabel = this.deepMerge(queryTarget, 'itemStyle.emphasis.label');
        var nTextStyle = nLabel.textStyle || {};
        var eTextStyle = eLabel.textStyle || {};
        if (nLabel.show) {
            var style = tarShape.style;
            style.text = this._getLabelText(serie, data, name, 'normal');
            style.textPosition = nLabel.position == null ? orient === 'horizontal' ? 'right' : 'top' : nLabel.position;
            style.textColor = nTextStyle.color;
            style.textFont = this.getFont(nTextStyle);
            style.textAlign = nTextStyle.align;
            style.textBaseline = nTextStyle.baseline;
        }
        if (eLabel.show) {
            var highlightStyle = tarShape.highlightStyle;
            highlightStyle.text = this._getLabelText(serie, data, name, 'emphasis');
            highlightStyle.textPosition = nLabel.show ? tarShape.style.textPosition : eLabel.position == null ? orient === 'horizontal' ? 'right' : 'top' : eLabel.position;
            highlightStyle.textColor = eTextStyle.color;
            highlightStyle.textFont = this.getFont(eTextStyle);
            highlightStyle.textAlign = eTextStyle.align;
            highlightStyle.textBaseline = eTextStyle.baseline;
        }
        return tarShape;
    },
    /**
         * 根据lable.format计算label text
         */
    _getLabelText: function (serie, data, name, status) {
        var formatter = this.deepQuery([
            data,
            serie
        ], 'itemStyle.' + status + '.label.formatter');
        if (!formatter && status === 'emphasis') {
            // emphasis时需要看看normal下是否有formatter
            formatter = this.deepQuery([
                data,
                serie
            ], 'itemStyle.normal.label.formatter');
        }
        var value = this.getDataFromOption(data, '-');
        if (formatter) {
            if (typeof formatter === 'function') {
                return formatter.call(this.myChart, {
                    seriesName: serie.name,
                    series: serie,
                    name: name,
                    value: value,
                    data: data,
                    status: status
                });
            } else if (typeof formatter === 'string') {
                formatter = formatter.replace('{a}', '{a0}').replace('{b}', '{b0}').replace('{c}', '{c0}').replace('{a0}', serie.name).replace('{b0}', name).replace('{c0}', this.numAddCommas(value));
                return formatter;
            }
        } else {
            if (value instanceof Array) {
                return value[2] != null ? this.numAddCommas(value[2]) : value[0] + ' , ' + value[1];
            } else {
                return this.numAddCommas(value);
            }
        }
    },
    /**
         * 标线标注 
         */
    buildMark: function (seriesIndex) {
        var serie = this.series[seriesIndex];
        if (this.selectedMap[serie.name]) {
            serie.markLine && this._buildMarkLine(seriesIndex);
            serie.markPoint && this._buildMarkPoint(seriesIndex);
        }
    },
    /**
         * 标注逻辑
         */
    _buildMarkPoint: function (seriesIndex) {
        var attachStyle = (this.markAttachStyle || {})[seriesIndex];
        var serie = this.series[seriesIndex];
        var mpData;
        var pos;
        var markPoint = zrUtil.clone(serie.markPoint);
        for (var i = 0, l = markPoint.data.length; i < l; i++) {
            mpData = markPoint.data[i];
            pos = this.getMarkCoord(seriesIndex, mpData);
            mpData.x = mpData.x != null ? mpData.x : pos[0];
            mpData.y = mpData.y != null ? mpData.y : pos[1];
            if (mpData.type && (mpData.type === 'max' || mpData.type === 'min')) {
                // 特殊值内置支持
                mpData.value = pos[3];
                mpData.name = mpData.name || mpData.type;
                mpData.symbolSize = mpData.symbolSize || zrArea.getTextWidth(pos[3], this.getFont()) / 2 + 5;
            }
        }
        var shapeList = this._markPoint(seriesIndex, markPoint);
        for (var i = 0, l = shapeList.length; i < l; i++) {
            var tarShape = shapeList[i];
            tarShape.zlevel = serie.zlevel;
            tarShape.z = serie.z + 1;
            for (var key in attachStyle) {
                tarShape[key] = zrUtil.clone(attachStyle[key]);
            }
            this.shapeList.push(tarShape);
        }
        // 个别特殊图表需要自己addShape
        if (this.type === ecConfig.CHART_TYPE_FORCE || this.type === ecConfig.CHART_TYPE_CHORD) {
            for (var i = 0, l = shapeList.length; i < l; i++) {
                this.zr.addShape(shapeList[i]);
            }
        }
    },
    /**
         * 标线逻辑
         */
    _buildMarkLine: function (seriesIndex) {
        var attachStyle = (this.markAttachStyle || {})[seriesIndex];
        var serie = this.series[seriesIndex];
        var pos;
        var markLine = zrUtil.clone(serie.markLine);
        for (var i = 0, l = markLine.data.length; i < l; i++) {
            var mlData = markLine.data[i];
            if (mlData.type && (mlData.type === 'max' || mlData.type === 'min' || mlData.type === 'average')) {
                // 特殊值内置支持
                pos = this.getMarkCoord(seriesIndex, mlData);
                markLine.data[i] = [
                    zrUtil.clone(mlData),
                    {}
                ];
                markLine.data[i][0].name = mlData.name || mlData.type;
                markLine.data[i][0].value = mlData.type !== 'average' ? pos[3] : +pos[3].toFixed(markLine.precision != null ? markLine.precision : this.deepQuery([
                    this.ecTheme,
                    ecConfig
                ], 'markLine.precision'));
                pos = pos[2];
                mlData = [
                    {},
                    {}
                ];
            } else {
                pos = [
                    this.getMarkCoord(seriesIndex, mlData[0]),
                    this.getMarkCoord(seriesIndex, mlData[1])
                ];
            }
            if (pos == null || pos[0] == null || pos[1] == null) {
                // 不在显示区域内
                continue;
            }
            markLine.data[i][0].x = mlData[0].x != null ? mlData[0].x : pos[0][0];
            markLine.data[i][0].y = mlData[0].y != null ? mlData[0].y : pos[0][1];
            markLine.data[i][1].x = mlData[1].x != null ? mlData[1].x : pos[1][0];
            markLine.data[i][1].y = mlData[1].y != null ? mlData[1].y : pos[1][1];
        }
        var shapeList = this._markLine(seriesIndex, markLine);
        var isLarge = markLine.large;
        if (isLarge) {
            var shapeBundle = new ShapeBundle({ style: { shapeList: shapeList } });
            var firstShape = shapeList[0];
            if (firstShape) {
                zrUtil.merge(shapeBundle.style, firstShape.style);
                zrUtil.merge(shapeBundle.highlightStyle = {}, firstShape.highlightStyle);
                shapeBundle.style.brushType = 'stroke';
                shapeBundle.zlevel = serie.zlevel;
                shapeBundle.z = serie.z + 1;
                shapeBundle.hoverable = false;
                for (var key in attachStyle) {
                    shapeBundle[key] = zrUtil.clone(attachStyle[key]);
                }
            }
            this.shapeList.push(shapeBundle);
            this.zr.addShape(shapeBundle);
            shapeBundle._mark = 'largeLine';
            var effect = markLine.effect;
            if (effect.show) {
                shapeBundle.effect = effect;
            }
        } else {
            for (var i = 0, l = shapeList.length; i < l; i++) {
                var tarShape = shapeList[i];
                tarShape.zlevel = serie.zlevel;
                tarShape.z = serie.z + 1;
                for (var key in attachStyle) {
                    tarShape[key] = zrUtil.clone(attachStyle[key]);
                }
                this.shapeList.push(tarShape);
            }
            // 个别特殊图表需要自己addShape
            if (this.type === ecConfig.CHART_TYPE_FORCE || this.type === ecConfig.CHART_TYPE_CHORD) {
                for (var i = 0, l = shapeList.length; i < l; i++) {
                    this.zr.addShape(shapeList[i]);
                }
            }
        }
    },
    /**
         * 标注多级控制构造
         */
    _markPoint: function (seriesIndex, mpOption) {
        var serie = this.series[seriesIndex];
        var component = this.component;
        zrUtil.merge(zrUtil.merge(mpOption, zrUtil.clone(this.ecTheme.markPoint || {})), zrUtil.clone(ecConfig.markPoint));
        mpOption.name = serie.name;
        var pList = [];
        var data = mpOption.data;
        var itemShape;
        var dataRange = component.dataRange;
        var legend = component.legend;
        var color;
        var value;
        var queryTarget;
        var nColor;
        var eColor;
        var effect;
        var zrWidth = this.zr.getWidth();
        var zrHeight = this.zr.getHeight();
        if (!mpOption.large) {
            for (var i = 0, l = data.length; i < l; i++) {
                if (data[i].x == null || data[i].y == null) {
                    continue;
                }
                value = data[i].value != null ? data[i].value : '';
                // 图例
                if (legend) {
                    color = legend.getColor(serie.name);
                }
                // 值域
                if (dataRange) {
                    color = isNaN(value) ? color : dataRange.getColor(value);
                    queryTarget = [
                        data[i],
                        mpOption
                    ];
                    nColor = this.deepQuery(queryTarget, 'itemStyle.normal.color') || color;
                    eColor = this.deepQuery(queryTarget, 'itemStyle.emphasis.color') || nColor;
                    // 有值域，并且值域返回null且用户没有自己定义颜色，则隐藏这个mark
                    if (nColor == null && eColor == null) {
                        continue;
                    }
                }
                color = color == null ? this.zr.getColor(seriesIndex) : color;
                // 标准化一些参数
                data[i].tooltip = data[i].tooltip || mpOption.tooltip || { trigger: 'item' };
                // tooltip.trigger指定为item
                data[i].name = data[i].name != null ? data[i].name : '';
                data[i].value = value;
                // 复用getSymbolShape
                itemShape = this.getSymbolShape(mpOption, seriesIndex, // 系列 
                data[i], i, data[i].name, // 数据
                this.parsePercent(data[i].x, zrWidth), // 坐标
                this.parsePercent(data[i].y, zrHeight), // 坐标
                'pin', color, // 默认symbol和color
                'rgba(0,0,0,0)', 'horizontal'    // 走向，用于默认文字定位
);
                itemShape._mark = 'point';
                effect = this.deepMerge([
                    data[i],
                    mpOption
                ], 'effect');
                if (effect.show) {
                    itemShape.effect = effect;
                }
                if (serie.type === ecConfig.CHART_TYPE_MAP) {
                    itemShape._geo = this.getMarkGeo(data[i]);
                }
                // 重新pack一下数据
                ecData.pack(itemShape, serie, seriesIndex, data[i], i, data[i].name, value);
                pList.push(itemShape);
            }
        } else {
            // 大规模MarkPoint
            itemShape = this.getLargeMarkPointShape(seriesIndex, mpOption);
            itemShape._mark = 'largePoint';
            itemShape && pList.push(itemShape);
        }
        return pList;
    },
    /**
         * 标线多级控制构造
         */
    _markLine: function () {
        function normalizeOptionValue(mlOption, key) {
            mlOption[key] = mlOption[key] instanceof Array ? mlOption[key].length > 1 ? mlOption[key] : [
                mlOption[key][0],
                mlOption[key][0]
            ] : [
                mlOption[key],
                mlOption[key]
            ];
        }
        return function (seriesIndex, mlOption) {
            var serie = this.series[seriesIndex];
            var component = this.component;
            var dataRange = component.dataRange;
            var legend = component.legend;
            zrUtil.merge(zrUtil.merge(mlOption, zrUtil.clone(this.ecTheme.markLine || {})), zrUtil.clone(ecConfig.markLine));
            var defaultColor = legend ? legend.getColor(serie.name) : this.zr.getColor(seriesIndex);
            // 标准化一些同时支持Array和String的参数
            normalizeOptionValue(mlOption, 'symbol');
            normalizeOptionValue(mlOption, 'symbolSize');
            normalizeOptionValue(mlOption, 'symbolRotate');
            // Normalize and filter data
            var data = mlOption.data;
            var edges = [];
            var zrWidth = this.zr.getWidth();
            var zrHeight = this.zr.getHeight();
            for (var i = 0; i < data.length; i++) {
                var mlData = data[i];
                if (isCoordAvailable(mlData[0]) && isCoordAvailable(mlData[1])) {
                    // 组装一个mergeData
                    var mergeData = this.deepMerge(mlData);
                    var queryTarget = [
                        mergeData,
                        mlOption
                    ];
                    var color = defaultColor;
                    var value = mergeData.value != null ? mergeData.value : '';
                    // 值域
                    if (dataRange) {
                        color = isNaN(value) ? color : dataRange.getColor(value);
                        var nColor = this.deepQuery(queryTarget, 'itemStyle.normal.color') || color;
                        var eColor = this.deepQuery(queryTarget, 'itemStyle.emphasis.color') || nColor;
                        // 有值域，并且值域返回null且用户没有自己定义颜色，则隐藏这个mark
                        if (nColor == null && eColor == null) {
                            continue;
                        }
                    }
                    // 标准化一些参数
                    mlData[0].tooltip = mergeData.tooltip || mlOption.tooltip || { trigger: 'item' };
                    // tooltip.trigger指定为item
                    mlData[0].name = mlData[0].name || '';
                    mlData[1].name = mlData[1].name || '';
                    mlData[0].value = value;
                    edges.push({
                        points: [
                            [
                                this.parsePercent(mlData[0].x, zrWidth),
                                this.parsePercent(mlData[0].y, zrHeight)
                            ],
                            [
                                this.parsePercent(mlData[1].x, zrWidth),
                                this.parsePercent(mlData[1].y, zrHeight)
                            ]
                        ],
                        rawData: mlData,
                        color: color
                    });
                }
            }
            var enableBundling = this.query(mlOption, 'bundling.enable');
            if (enableBundling) {
                var edgeBundling = new EdgeBundling();
                edgeBundling.maxTurningAngle = this.query(mlOption, 'bundling.maxTurningAngle') / 180 * Math.PI;
                edges = edgeBundling.run(edges);
            }
            mlOption.name = serie.name;
            var shapeList = [];
            for (var i = 0, l = edges.length; i < l; i++) {
                var edge = edges[i];
                var rawEdge = edge.rawEdge || edge;
                var mlData = rawEdge.rawData;
                var value = mlData.value != null ? mlData.value : '';
                var itemShape = this.getMarkLineShape(mlOption, seriesIndex, mlData, i, edge.points, enableBundling, rawEdge.color);
                itemShape._mark = 'line';
                var effect = this.deepMerge([
                    mlData[0],
                    mlData[1],
                    mlOption
                ], 'effect');
                if (effect.show) {
                    itemShape.effect = effect;
                    itemShape.effect.large = mlOption.large;
                }
                if (serie.type === ecConfig.CHART_TYPE_MAP) {
                    itemShape._geo = [
                        this.getMarkGeo(mlData[0]),
                        this.getMarkGeo(mlData[1])
                    ];
                }
                // 重新pack一下数据
                ecData.pack(itemShape, serie, seriesIndex, mlData[0], i, mlData[0].name + (mlData[1].name !== '' ? ' > ' + mlData[1].name : ''), value);
                shapeList.push(itemShape);
            }
            return shapeList;
        };
    }(),
    getMarkCoord: function () {
        // 无转换位置
        return [
            0,
            0
        ];
    },
    /**
         * symbol构造器 
         */
    getSymbolShape: function (serie, seriesIndex, data, dataIndex, name, x, y, symbol, color, emptyColor, orient) {
        var queryTarget = [
            data,
            serie
        ];
        var value = this.getDataFromOption(data, '-');
        symbol = this.deepQuery(queryTarget, 'symbol') || symbol;
        var symbolSize = this.deepQuery(queryTarget, 'symbolSize');
        symbolSize = typeof symbolSize === 'function' ? symbolSize(value) : symbolSize;
        if (typeof symbolSize === 'number') {
            symbolSize = [
                symbolSize,
                symbolSize
            ];
        }
        var symbolRotate = this.deepQuery(queryTarget, 'symbolRotate');
        var normal = this.deepMerge(queryTarget, 'itemStyle.normal');
        var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis');
        var nBorderWidth = normal.borderWidth != null ? normal.borderWidth : normal.lineStyle && normal.lineStyle.width;
        if (nBorderWidth == null) {
            nBorderWidth = symbol.match('empty') ? 2 : 0;
        }
        var eBorderWidth = emphasis.borderWidth != null ? emphasis.borderWidth : emphasis.lineStyle && emphasis.lineStyle.width;
        if (eBorderWidth == null) {
            eBorderWidth = nBorderWidth + 2;
        }
        var nColor = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data);
        var eColor = this.getItemStyleColor(emphasis.color, seriesIndex, dataIndex, data);
        var width = symbolSize[0];
        var height = symbolSize[1];
        var itemShape = new IconShape({
            style: {
                iconType: symbol.replace('empty', '').toLowerCase(),
                x: x - width,
                y: y - height,
                width: width * 2,
                height: height * 2,
                brushType: 'both',
                color: symbol.match('empty') ? emptyColor : nColor || color,
                strokeColor: normal.borderColor || nColor || color,
                lineWidth: nBorderWidth
            },
            highlightStyle: {
                color: symbol.match('empty') ? emptyColor : eColor || nColor || color,
                strokeColor: emphasis.borderColor || normal.borderColor || eColor || nColor || color,
                lineWidth: eBorderWidth
            },
            clickable: this.deepQuery(queryTarget, 'clickable')
        });
        if (symbol.match('image')) {
            itemShape.style.image = symbol.replace(new RegExp('^image:\\/\\/'), '');
            itemShape = new ImageShape({
                style: itemShape.style,
                highlightStyle: itemShape.highlightStyle,
                clickable: this.deepQuery(queryTarget, 'clickable')
            });
        }
        if (symbolRotate != null) {
            itemShape.rotation = [
                symbolRotate * Math.PI / 180,
                x,
                y
            ];
        }
        if (symbol.match('star')) {
            itemShape.style.iconType = 'star';
            itemShape.style.n = symbol.replace('empty', '').replace('star', '') - 0 || 5;
        }
        if (symbol === 'none') {
            itemShape.invisible = true;
            itemShape.hoverable = false;
        }
        /*
            if (this.deepQuery([data, serie, option], 'calculable')) {
                this.setCalculable(itemShape);
                itemShape.draggable = true;
            }
            */
        itemShape = this.addLabel(itemShape, serie, data, name, orient);
        if (symbol.match('empty')) {
            if (itemShape.style.textColor == null) {
                itemShape.style.textColor = itemShape.style.strokeColor;
            }
            if (itemShape.highlightStyle.textColor == null) {
                itemShape.highlightStyle.textColor = itemShape.highlightStyle.strokeColor;
            }
        }
        ecData.pack(itemShape, serie, seriesIndex, data, dataIndex, name);
        itemShape._x = x;
        itemShape._y = y;
        itemShape._dataIndex = dataIndex;
        itemShape._seriesIndex = seriesIndex;
        return itemShape;
    },
    /**
         * 标线构造器 
         */
    getMarkLineShape: function (mlOption, seriesIndex, data, dataIndex, points, bundling, color) {
        var value0 = data[0].value != null ? data[0].value : '-';
        var value1 = data[1].value != null ? data[1].value : '-';
        var symbol = [
            data[0].symbol || mlOption.symbol[0],
            data[1].symbol || mlOption.symbol[1]
        ];
        var symbolSize = [
            data[0].symbolSize || mlOption.symbolSize[0],
            data[1].symbolSize || mlOption.symbolSize[1]
        ];
        symbolSize[0] = typeof symbolSize[0] === 'function' ? symbolSize[0](value0) : symbolSize[0];
        symbolSize[1] = typeof symbolSize[1] === 'function' ? symbolSize[1](value1) : symbolSize[1];
        var symbolRotate = [
            this.query(data[0], 'symbolRotate') || mlOption.symbolRotate[0],
            this.query(data[1], 'symbolRotate') || mlOption.symbolRotate[1]
        ];
        //console.log(symbol, symbolSize, symbolRotate);
        var queryTarget = [
            data[0],
            data[1],
            mlOption
        ];
        var normal = this.deepMerge(queryTarget, 'itemStyle.normal');
        normal.color = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data);
        var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis');
        emphasis.color = this.getItemStyleColor(emphasis.color, seriesIndex, dataIndex, data);
        var nlineStyle = normal.lineStyle;
        var elineStyle = emphasis.lineStyle;
        var nBorderWidth = nlineStyle.width;
        if (nBorderWidth == null) {
            nBorderWidth = normal.borderWidth;
        }
        var eBorderWidth = elineStyle.width;
        if (eBorderWidth == null) {
            eBorderWidth = emphasis.borderWidth != null ? emphasis.borderWidth : nBorderWidth + 2;
        }
        var smoothness = this.deepQuery(queryTarget, 'smoothness');
        if (!this.deepQuery(queryTarget, 'smooth')) {
            smoothness = 0;
        }
        var ShapeCtor = bundling ? PolylineShape : MarkLineShape;
        var itemShape = new ShapeCtor({
            style: {
                symbol: symbol,
                symbolSize: symbolSize,
                symbolRotate: symbolRotate,
                // data: [data[0].name,data[1].name],
                brushType: 'both',
                lineType: nlineStyle.type,
                shadowColor: nlineStyle.shadowColor || nlineStyle.color || normal.borderColor || normal.color || color,
                shadowBlur: nlineStyle.shadowBlur,
                shadowOffsetX: nlineStyle.shadowOffsetX,
                shadowOffsetY: nlineStyle.shadowOffsetY,
                color: normal.color || color,
                strokeColor: nlineStyle.color || normal.borderColor || normal.color || color,
                lineWidth: nBorderWidth,
                symbolBorderColor: normal.borderColor || normal.color || color,
                symbolBorder: normal.borderWidth
            },
            highlightStyle: {
                shadowColor: elineStyle.shadowColor,
                shadowBlur: elineStyle.shadowBlur,
                shadowOffsetX: elineStyle.shadowOffsetX,
                shadowOffsetY: elineStyle.shadowOffsetY,
                color: emphasis.color || normal.color || color,
                strokeColor: elineStyle.color || nlineStyle.color || emphasis.borderColor || normal.borderColor || emphasis.color || normal.color || color,
                lineWidth: eBorderWidth,
                symbolBorderColor: emphasis.borderColor || normal.borderColor || emphasis.color || normal.color || color,
                symbolBorder: emphasis.borderWidth == null ? normal.borderWidth + 2 : emphasis.borderWidth
            },
            clickable: this.deepQuery(queryTarget, 'clickable')
        });
        var shapeStyle = itemShape.style;
        if (bundling) {
            shapeStyle.pointList = points;
            shapeStyle.smooth = smoothness;
        } else {
            shapeStyle.xStart = points[0][0];
            shapeStyle.yStart = points[0][1];
            shapeStyle.xEnd = points[1][0];
            shapeStyle.yEnd = points[1][1];
            shapeStyle.curveness = smoothness;
            itemShape.updatePoints(itemShape.style);
        }
        itemShape = this.addLabel(itemShape, mlOption, data[0], data[0].name + ' : ' + data[1].name);
        return itemShape;
    },
    /**
         * 大规模标注构造器 
         */
    getLargeMarkPointShape: function (seriesIndex, mpOption) {
        var serie = this.series[seriesIndex];
        var component = this.component;
        var data = mpOption.data;
        var itemShape;
        var dataRange = component.dataRange;
        var legend = component.legend;
        var color;
        var value;
        var queryTarget = [
            data[0],
            mpOption
        ];
        var nColor;
        var eColor;
        var effect;
        // 图例
        if (legend) {
            color = legend.getColor(serie.name);
        }
        // 值域
        if (dataRange) {
            value = data[0].value != null ? data[0].value : '';
            color = isNaN(value) ? color : dataRange.getColor(value);
            nColor = this.deepQuery(queryTarget, 'itemStyle.normal.color') || color;
            eColor = this.deepQuery(queryTarget, 'itemStyle.emphasis.color') || nColor;
            // 有值域，并且值域返回null且用户没有自己定义颜色，则隐藏这个mark
            if (nColor == null && eColor == null) {
                return;
            }
        }
        color = this.deepMerge(queryTarget, 'itemStyle.normal').color || color;
        var symbol = this.deepQuery(queryTarget, 'symbol') || 'circle';
        symbol = symbol.replace('empty', '').replace(/\d/g, '');
        effect = this.deepMerge([
            data[0],
            mpOption
        ], 'effect');
        var devicePixelRatio = window.devicePixelRatio || 1;
        //console.log(data)
        itemShape = new SymbolShape({
            style: {
                pointList: data,
                color: color,
                strokeColor: color,
                shadowColor: effect.shadowColor || color,
                shadowBlur: (effect.shadowBlur != null ? effect.shadowBlur : 8) * devicePixelRatio,
                size: this.deepQuery(queryTarget, 'symbolSize'),
                iconType: symbol,
                brushType: 'fill',
                lineWidth: 1
            },
            draggable: false,
            hoverable: false
        });
        if (effect.show) {
            itemShape.effect = effect;
        }
        return itemShape;
    },
    backupShapeList: function () {
        if (this.shapeList && this.shapeList.length > 0) {
            this.lastShapeList = this.shapeList;
            this.shapeList = [];
        } else {
            this.lastShapeList = [];
        }
    },
    addShapeList: function () {
        var maxLenth = this.option.animationThreshold / (this.canvasSupported ? 2 : 4);
        var lastShapeList = this.lastShapeList;
        var shapeList = this.shapeList;
        var isUpdate = lastShapeList.length > 0;
        var duration = isUpdate ? this.query(this.option, 'animationDurationUpdate') : this.query(this.option, 'animationDuration');
        var easing = this.query(this.option, 'animationEasing');
        var delay;
        var key;
        var oldMap = {};
        var newMap = {};
        if (this.option.animation && !this.option.renderAsImage && shapeList.length < maxLenth && !this.motionlessOnce) {
            // 通过已有的shape做动画过渡
            for (var i = 0, l = lastShapeList.length; i < l; i++) {
                key = this._getAnimationKey(lastShapeList[i]);
                if (key.match('undefined')) {
                    this.zr.delShape(lastShapeList[i].id);    // 非关键元素直接删除
                } else {
                    key += lastShapeList[i].type;
                    // https://github.com/ecomfe/echarts/issues/1219#issuecomment-71987602
                    // 响应中断可能产生的重复元素
                    if (oldMap[key]) {
                        this.zr.delShape(lastShapeList[i].id);
                    } else {
                        oldMap[key] = lastShapeList[i];
                    }
                }
            }
            for (var i = 0, l = shapeList.length; i < l; i++) {
                key = this._getAnimationKey(shapeList[i]);
                if (key.match('undefined')) {
                    this.zr.addShape(shapeList[i]);    // 非关键元素直接添加
                } else {
                    key += shapeList[i].type;
                    newMap[key] = shapeList[i];
                }
            }
            for (key in oldMap) {
                if (!newMap[key]) {
                    // 新的没有 删除
                    this.zr.delShape(oldMap[key].id);
                }
            }
            for (key in newMap) {
                if (oldMap[key]) {
                    // 新旧都有 动画过渡
                    this.zr.delShape(oldMap[key].id);
                    this._animateMod(oldMap[key], newMap[key], duration, easing, 0, isUpdate);
                } else {
                    // 新有旧没有  添加并动画过渡
                    //this._animateAdd(newMap[key], duration, easing);
                    delay = (this.type == ecConfig.CHART_TYPE_LINE || this.type == ecConfig.CHART_TYPE_RADAR) && key.indexOf('icon') !== 0 ? duration / 2 : 0;
                    this._animateMod(false, newMap[key], duration, easing, delay, isUpdate);
                }
            }
            this.zr.refresh();
            this.animationEffect();
        } else {
            this.motionlessOnce = false;
            // clear old
            this.zr.delShape(lastShapeList);
            // 直接添加
            for (var i = 0, l = shapeList.length; i < l; i++) {
                this.zr.addShape(shapeList[i]);
            }
        }
    },
    _getAnimationKey: function (shape) {
        if (this.type != ecConfig.CHART_TYPE_MAP && this.type != ecConfig.CHART_TYPE_TREEMAP && this.type != ecConfig.CHART_TYPE_VENN && this.type != ecConfig.CHART_TYPE_TREE) {
            return ecData.get(shape, 'seriesIndex') + '_' + ecData.get(shape, 'dataIndex') + (shape._mark ? shape._mark : '') + (this.type === ecConfig.CHART_TYPE_RADAR ? ecData.get(shape, 'special') : '');
        } else {
            return ecData.get(shape, 'seriesIndex') + '_' + ecData.get(shape, 'dataIndex') + (shape._mark ? shape._mark : 'undefined');
        }
    },
    /**
         * 动画过渡 
         */
    _animateMod: function (oldShape, newShape, duration, easing, delay, isUpdate) {
        switch (newShape.type) {
        case 'polyline':
        case 'half-smooth-polygon':
            ecAnimation.pointList(this.zr, oldShape, newShape, duration, easing);
            break;
        case 'rectangle':
            ecAnimation.rectangle(this.zr, oldShape, newShape, duration, easing);
            break;
        case 'image':
        case 'icon':
            ecAnimation.icon(this.zr, oldShape, newShape, duration, easing, delay);
            break;
        case 'candle':
            if (!isUpdate) {
                ecAnimation.candle(this.zr, oldShape, newShape, duration, easing);
            } else {
                this.zr.addShape(newShape);
            }
            break;
        case 'ring':
        case 'sector':
        case 'circle':
            if (!isUpdate) {
                // 进入动画，加旋转
                ecAnimation.ring(this.zr, oldShape, newShape, duration + (ecData.get(newShape, 'dataIndex') || 0) % 20 * 100, easing);
            } else if (newShape.type === 'sector') {
                ecAnimation.sector(this.zr, oldShape, newShape, duration, easing);
            } else {
                this.zr.addShape(newShape);
            }
            break;
        case 'text':
            ecAnimation.text(this.zr, oldShape, newShape, duration, easing);
            break;
        case 'polygon':
            if (!isUpdate) {
                ecAnimation.polygon(this.zr, oldShape, newShape, duration, easing);
            } else {
                ecAnimation.pointList(this.zr, oldShape, newShape, duration, easing);
            }
            break;
        case 'ribbon':
            ecAnimation.ribbon(this.zr, oldShape, newShape, duration, easing);
            break;
        case 'gauge-pointer':
            ecAnimation.gaugePointer(this.zr, oldShape, newShape, duration, easing);
            break;
        case 'mark-line':
            ecAnimation.markline(this.zr, oldShape, newShape, duration, easing);
            break;
        case 'bezier-curve':
        case 'line':
            ecAnimation.line(this.zr, oldShape, newShape, duration, easing);
            break;
        default:
            this.zr.addShape(newShape);
            break;
        }
    },
    /**
         * 标注动画
         * @param {number} duration 时长
         * @param {string=} easing 缓动效果
         * @param {Array=} shapeList 指定特效对象，不指定默认使用this.shapeList
         */
    animationMark: function (duration, easing, shapeList) {
        var shapeList = shapeList || this.shapeList;
        for (var i = 0, l = shapeList.length; i < l; i++) {
            if (!shapeList[i]._mark) {
                continue;
            }
            this._animateMod(false, shapeList[i], duration, easing, 0, true);
        }
        this.animationEffect(shapeList);
    },
    /**
         * 特效动画
         * @param {Array=} shapeList 指定特效对象，不知道默认使用this.shapeList
         */
    animationEffect: function (shapeList) {
        !shapeList && this.clearEffectShape();
        shapeList = shapeList || this.shapeList;
        if (shapeList == null) {
            return;
        }
        var zlevel = ecConfig.EFFECT_ZLEVEL;
        if (this.canvasSupported) {
            this.zr.modLayer(zlevel, {
                motionBlur: true,
                lastFrameAlpha: this.option.effectBlendAlpha || ecConfig.effectBlendAlpha
            });
        }
        var shape;
        for (var i = 0, l = shapeList.length; i < l; i++) {
            shape = shapeList[i];
            if (!(shape._mark && shape.effect && shape.effect.show && ecEffect[shape._mark])) {
                continue;
            }
            ecEffect[shape._mark](this.zr, this.effectList, shape, zlevel);
            this.effectList[this.effectList.length - 1]._mark = shape._mark;
        }
    },
    clearEffectShape: function (clearMotionBlur) {
        var effectList = this.effectList;
        if (this.zr && effectList && effectList.length > 0) {
            clearMotionBlur && this.zr.modLayer(ecConfig.EFFECT_ZLEVEL, { motionBlur: false });
            this.zr.delShape(effectList);
            // 手动清除不会被 zr 自动清除的动画控制器
            for (var i = 0; i < effectList.length; i++) {
                if (effectList[i].effectAnimator) {
                    effectList[i].effectAnimator.stop();
                }
            }
        }
        this.effectList = [];
    },
    /**
         * 动态标线标注添加
         * @param {number} seriesIndex 系列索引
         * @param {Object} markData 标线标注对象，支持多个
         * @param {string} markType 标线标注类型
         */
    addMark: function (seriesIndex, markData, markType) {
        var serie = this.series[seriesIndex];
        if (this.selectedMap[serie.name]) {
            var duration = this.query(this.option, 'animationDurationUpdate');
            var easing = this.query(this.option, 'animationEasing');
            // 备份，复用_buildMarkX
            var oriMarkData = serie[markType].data;
            var lastLength = this.shapeList.length;
            serie[markType].data = markData.data;
            this['_build' + markType.replace('m', 'M')](seriesIndex);
            if (this.option.animation && !this.option.renderAsImage) {
                // animationMark就会addShape
                this.animationMark(duration, easing, this.shapeList.slice(lastLength));
            } else {
                for (var i = lastLength, l = this.shapeList.length; i < l; i++) {
                    this.zr.addShape(this.shapeList[i]);
                }
                this.zr.refreshNextFrame();
            }
            // 还原，复用_buildMarkX
            serie[markType].data = oriMarkData;
        }
    },
    /**
         * 动态标线标注删除
         * @param {number} seriesIndex 系列索引
         * @param {string} markName 标线标注名称
         * @param {string} markType 标线标注类型
         */
    delMark: function (seriesIndex, markName, markType) {
        markType = markType.replace('mark', '').replace('large', '').toLowerCase();
        var serie = this.series[seriesIndex];
        if (this.selectedMap[serie.name]) {
            var needRefresh = false;
            var shapeList = [
                this.shapeList,
                this.effectList
            ];
            var len = 2;
            while (len--) {
                for (var i = 0, l = shapeList[len].length; i < l; i++) {
                    if (shapeList[len][i]._mark == markType && ecData.get(shapeList[len][i], 'seriesIndex') == seriesIndex && ecData.get(shapeList[len][i], 'name') == markName) {
                        this.zr.delShape(shapeList[len][i].id);
                        shapeList[len].splice(i, 1);
                        needRefresh = true;
                        break;
                    }
                }
            }
            needRefresh && this.zr.refreshNextFrame();
        }
    }
};
zrUtil.inherits(Base, ComponentBase);
module.exports = Base || module.exports;;